#include "baseStation.hpp"
#include "sysapi.hpp"
#include "core.hpp"
#include "sta_cmd.hpp"

#define LOG_TAG "driver_station"
#define LOG_LVL LOG_LVL_DBG
#include <ulog.h>

robot_station* robot_station::p = RT_NULL;

robot_station& robot_station::instance()
{
    if (p == NULL)
    {
        p = new robot_station(STATION_UART_NAME, STATION_UART_HZ);
        p->open();
    }
    return *p;
}

void rxuart_thread_entry(void *parameter)
{
    robot_station* thisp = (robot_station*)parameter;
	char ch;
	int8_t rec_num;
	int8_t all_data_num;

    /* if data is transmitting, value = 1 */ 
    uint8_t data_recing = 0;

    while(1)
    {
		rec_num = 0;
		all_data_num = 0;
		
		while(all_data_num==0 || rec_num!=all_data_num){ 	
                
			while(thisp->read(-1, &ch, 1) == 0)
			{
				rt_sem_take(&thisp->irq_rx_sem, RT_WAITING_FOREVER);
			}

			if(data_recing==0 && ch==0x3A) //receive the head
			{
				data_recing = 1;	//note
				rec_num = 0;
				all_data_num = 0;
			}
			
			if(data_recing == 1)
			{
				rec_num++; 
				if(rec_num==4) //find 
				{
					if(ch > 4)
						goto __rec_reset;

					all_data_num = 12; 
					thisp->rec_databytes = all_data_num;
				}
				thisp->rec_databuf[rec_num-1]=ch;
			}
		}
		
		if(thisp->rec_databuf[thisp->rec_databytes-1]==0x0A \
            && thisp->rec_databuf[thisp->rec_databytes-2]==0x0D)
		{
			data_recing = 0;
			rt_sem_release(&thisp->finish_rec_sem); // finish
			goto __rec_reset;
		}
			
		__rec_reset:
			rec_num = 0;
			all_data_num = 0;
	}
}

void directive_run_thread_entry(void* parameter)
{
    robot_station* thisp = (robot_station*)parameter;
	robot_func* ihandle;

	uint16_t crc;
	uint8_t devaddr_rec;
    uint8_t funcnum_rec;
	
	while(1)
	{
		rt_sem_take(&thisp->finish_rec_sem, RT_WAITING_FOREVER);

		crc = get_crc16(&thisp->rec_databuf[1], thisp->rec_databytes-5);
		
		if((uint8_t)(crc>>8)!=thisp->rec_databuf[thisp->rec_databytes-4] ||	\
			(uint8_t)crc!=thisp->rec_databuf[thisp->rec_databytes-3])
			continue;

		int i;
		
		for(i=0; i<FUNC_LIST_LEN; i++)
		{
			ihandle = func_list[i];
			devaddr_rec = thisp->rec_databuf[1];
			funcnum_rec = thisp->rec_databuf[2];
			
			if(devaddr_rec==ihandle->dev_num && funcnum_rec==ihandle->fu_num && ihandle->func!=0) //match
			{
                cmd_line_group in(thisp->rec_databuf, thisp->rec_databytes);
				cmd_line_group& out = ihandle->func(in);

				for(int i=0; i<out.num; i++)
				{
					rt_device_write(thisp->hw_handle, 0, out.group[i].cmd, out.group[i].len);
				}
			}
		}
		if(i == FUNC_LIST_LEN)
			 LOG_W("directive_run_thread_entry-cmd not defined!");
	}
}

int robot_station::get_data(uint8_t* buf, int len)
{
    if(len < rec_databytes)
    {
        LOG_W("buf is small");
        return 0;
    }
    else
    {
        rt_memcpy(buf, rec_databuf, rec_databytes);
        return rec_databytes;
    }
}

robot_station::robot_station(const char* name, uint32_t boud)\
    :robot_devuart(name, boud)
{
    rt_sem_init(&irq_rx_sem, "ciirq", 0, RT_IPC_FLAG_FIFO);
	rt_sem_init(&finish_rec_sem, "cirecf", 0, RT_IPC_FLAG_FIFO);

    rt_thread_t thread_uart = \
    rt_thread_create("cirec", rxuart_thread_entry, this, 1024, 25, 10);

    if (thread_uart != RT_NULL)
    {
        rt_thread_startup(thread_uart);
    }

	rt_thread_t thread_data = \
    rt_thread_create("ciexe", directive_run_thread_entry, this, 4096, 25, 10);

    if (thread_data != RT_NULL)
    {
        rt_thread_startup(thread_data);
    }
}